﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using AForge.Imaging.Filters;
using XnaFan.ImageComparison;
using Image = System.Drawing.Image;

namespace WindowsFormsGUI.Entidades
{
    public class ProcesadorDeImagen
    {
        #region Atributos

        private static int _centroMasaX = 0;
        private static int _centroMasaY = 0;
        private const string pathImagenesBase = @"D:\img\";
        private const string pathImagenContrasteNegro = @"D:\img\fondoNegro.jpg";
        private const string pathImagenContrasteBlanco = @"D:\img\fondoBlanco.jpg";
        private const string pathImagenes = @"D:\img\";
        private byte _threshold = 3;
        private const int error = 700;
        private List<Moneda> _listaMonedaBase;

        #endregion

        #region Propiedades

        public List<Moneda> MonedasBase
        {
            get { return _listaMonedaBase; }
        } 

        #endregion

        #region Constructor

        public ProcesadorDeImagen()
        {
            _listaMonedaBase = new List<Moneda>();
        }

        #endregion

        #region Metodos publicos

        public void Configurar()
        {
            // 5 centavos
            ProcesarDirectorioDeImagenesDeMoneda(@"D:\img\5c", DenominacionEnum.CincoCentavos);
            // 10 centavos
            ProcesarDirectorioDeImagenesDeMoneda(@"D:\img\10c", DenominacionEnum.DiezCentavos);
            // 25 centavos
            ProcesarDirectorioDeImagenesDeMoneda(@"D:\img\25c", DenominacionEnum.VeinticincoCentavos);
            // 50 centavos
            ProcesarDirectorioDeImagenesDeMoneda(@"D:\img\50c", DenominacionEnum.CincuentaCentavos);
            // 1 peso
            ProcesarDirectorioDeImagenesDeMoneda(@"D:\img\1p", DenominacionEnum.UnPeso);
        }

        public Moneda Clasificar(Image imagen)
        {
            //var bitmap = new Bitmap(imagen);
            //var recorte = ConvertirImagenAMapaDeBit(bitmap);
            var recorte = new Bitmap(imagen);
            var distancia = ObtenerDiferenciasConContraste(recorte);

            return CompararDistancias(distancia);
        }

        public Bitmap ConvertirImagenAMapaDeBit(Bitmap bitmap)
        {
            // blur
            var blur = AplicarBlur(bitmap);
            // escala de grises
            var gris = ObtenerEscalaDeGrises(blur);
            // binarizacion
            var binarizado = Binarizacion(gris);
            // centro de masa
            CalcularCentroDeMasa(binarizado);
            //// ecualizacion del histograma
            //var equalizado = EcualizacionDelHistograma(binarizado);
            // deteccion de bordes sobel
            var sobel = DeteccionDeBordesSobel(binarizado);
            // recortar imagen
            var recorte = RecortarImagen(sobel);

            var redimensionar = Redimensionar(recorte);

            return redimensionar;
        }

        public void ProcesarDirectorioDeImagenesDeMoneda(string path, DenominacionEnum denominacion)
        {
            var monedaCara = ProcesarDirectorioDeImagenesDeMoneda(path, denominacion, LadoMonedaEnum.Cara);
            var monedaSeca = ProcesarDirectorioDeImagenesDeMoneda(path, denominacion, LadoMonedaEnum.Seca);

            _listaMonedaBase.Add(monedaCara);
            _listaMonedaBase.Add(monedaSeca);
        }

        public Moneda ProcesarDirectorioDeImagenesDeMoneda(string path, DenominacionEnum denominacion, LadoMonedaEnum lado)
        {
            var lista = new List<Moneda>();
            var filtro = lado == LadoMonedaEnum.Cara ? "*c.jpg" : "*s.jpg";
            var imagenes = Directory.GetFiles(path, filtro);

            for (int i = 0; i < imagenes.Length; i++)
            {
                var moneda = new Moneda();

                moneda.Imagen = (Bitmap)Image.FromFile(imagenes[i]);
                moneda.Lado = lado;
                moneda.Nombre = denominacion;
                moneda.Distancia = ObtenerDiferenciasConContraste(moneda.Imagen);

                lista.Add(moneda);
            }

            var distanciaPromedio = ProcesarListaDeMonedas(lista);
            var monedaBase = new Moneda(denominacion, lado, distanciaPromedio);

            return monedaBase;
        }

        #endregion

        #region Metodos de soporte

        private Bitmap Redimensionar(Bitmap bitmap)
        {
            var filtro = new ResizeNearestNeighbor(100, 100);
            return filtro.Apply(bitmap);
        }

        private int ObtenerDiferenciasConContraste(Bitmap imagen)
        {
            var contraste = (Bitmap)Image.FromFile(pathImagenContrasteNegro);
            return ObtenerDiferenciasConContraste(imagen, contraste);
        }

        private int ObtenerDiferenciasConContraste(Bitmap imagen, Bitmap contraste)
        {
            var matrizImagen = imagen.GetDifferences(contraste);
            var distancia = 0;

            for (int i = 0; i < 16; i++)
            {
                for (int j = 0; j < 16; j++)
                {
                    distancia += Convert.ToInt32(matrizImagen[i, j]);
                }
            }

            return distancia;
        }

        private Moneda CompararDistancias(int distancia)
        {
            double distanciaMenor = 999999;
            Moneda moneda = null;

            foreach (var item in _listaMonedaBase)
            {
                var diferencia = Math.Abs(item.Distancia - distancia);

                if ((diferencia <= distanciaMenor) && (diferencia <= error))
                {
                    distanciaMenor = diferencia;
                    moneda = item;
                }
            }

            return moneda;
        }

        private Bitmap Cany(Bitmap bitmap)
        {
            var filtro = new CannyEdgeDetector();
            return filtro.Apply(bitmap);
        }

        private Bitmap ProcesarImagen(Bitmap bitmap)
        {
            var redimension = Redimensionar(bitmap);
            var gris = ObtenerEscalaDeGrises(redimension);
            var binarizacion = Binarizacion(gris);

            return binarizacion;
        }

        private Bitmap RecortarImagen(Bitmap bitmap)
        {
            int x0 = 0;
            int y0 = 0;
            int x1 = 0;
            int y1 = 0;
            int x2 = 0;
            int y2 = 0;
            int x3 = 0;
            int y3 = 0;

            bool bandera = false;

            // Recorrer hacia adelante y hacia abajo
            for (int width = 0; width < bitmap.Width; width++)
            {
                for (int height = 0; height < bitmap.Height; height++)
                {
                    var colorPixel = (bitmap.GetPixel(width, height));

                    if (colorPixel.ToArgb() == Color.White.ToArgb())
                    {
                        x0 = width;
                        y0 = height;

                        bandera = true;
                        break;
                    }
                }

                if (bandera)
                    break;
            }

            bandera = false;
            // Recorrer hacia abajo y hacia adelante
            for (int height = 0; height < bitmap.Height; height++)
            {
                for (int width = 0; width < bitmap.Width; width++)
                {
                    var colorPixel = (bitmap.GetPixel(width, height));

                    if (colorPixel.ToArgb() == Color.White.ToArgb())
                    {
                        x1 = width;
                        y1 = height;

                        bandera = true;
                        break;
                    }
                }

                if (bandera)
                    break;
            }

            bandera = false;
            // Recorrer hacia atras y hacia arriba
            for (int width = bitmap.Width - 1; width >= 0; width--)
            {
                for (int height = bitmap.Height - 1; height >= 0; height--)
                {
                    var colorPixel = (bitmap.GetPixel(width, height));

                    if (colorPixel.ToArgb() == Color.White.ToArgb())
                    {
                        x2 = width;
                        y2 = height;

                        bandera = true;
                        break;
                    }
                }

                if (bandera)
                    break;
            }

            bandera = false;
            // Recorrer hacia arriba y hacia atras
            for (int height = bitmap.Height - 1; height >= 0; height--)
            {
                for (int width = bitmap.Width - 1; width >= 0; width--)
                {
                    var colorPixel = (bitmap.GetPixel(width, height));

                    if (colorPixel.ToArgb() == Color.White.ToArgb())
                    {
                        x3 = width;
                        y3 = height;

                        bandera = true;
                        break;
                    }
                }

                if (bandera)
                    break;
            }

            var filtro = new Crop(new Rectangle(x0, y1, x2 - x0, y3 - y1));
            return filtro.Apply(bitmap);
        }

        private Bitmap AplicarBlur(Bitmap bitmap)
        {
            var filtro = new Blur();
            return filtro.Apply(bitmap);
        }

        private Bitmap ObtenerEscalaDeGrises(Bitmap bitmap)
        {
            var filtro = new Grayscale(0.2125, 0.7154, 0.0721);
            return filtro.Apply(bitmap);
        }

        private Bitmap Binarizacion(Bitmap bitmap)
        {
            var filtro = new Threshold();
            return filtro.Apply(bitmap);
        }

        private void CalcularCentroDeMasa(Bitmap bitmap)
        {
            _centroMasaX = 0;
            _centroMasaY = 0;

            var contadorPixelNegro = 0;
            var acumuladorX = 0;
            var acumuladorY = 0;

            for (int i = 0; i < bitmap.Width; i++)
            {
                for (int j = 0; j < bitmap.Height; j++)
                {
                    var colorPixel = (bitmap.GetPixel(i, j));

                    if (colorPixel.ToArgb() == Color.Black.ToArgb())
                    {
                        acumuladorX += i;
                        acumuladorY += j;

                        contadorPixelNegro++;
                    }
                }
            }

            if (contadorPixelNegro > 0)
            {
                _centroMasaX = acumuladorX / contadorPixelNegro;
                _centroMasaY = acumuladorY / contadorPixelNegro;
            }
        }

        private Bitmap EcualizacionDelHistograma(Bitmap bitmap)
        {
            var filtro = new HistogramEqualization();
            return filtro.Apply(bitmap);
        }

        private Bitmap DeteccionDeBordesSobel(Bitmap bitmap)
        {
            var filtro = new SobelEdgeDetector();
            return filtro.Apply(bitmap);
        }

        private void CalcularDistanciasBase()
        {
            foreach (var item in _listaMonedaBase)
            {
                var distancia = ObtenerDiferenciasConContraste(item.Imagen);
                item.Distancia = distancia;
            }
        }        

        private double ProcesarListaDeMonedas(IEnumerable<Moneda> lista)
        {
            var promedioDistancia = (from item in lista select item.Distancia).Average();
            return promedioDistancia;
        }

        #endregion
    }
}
